package top.lunarye.handler.deduplication.limit;

import cn.hutool.core.collection.CollUtil;
import org.springframework.stereotype.Service;
import top.lunarye.common.constant.MessageConstant;
import top.lunarye.common.domaim.TaskInfo;
import top.lunarye.handler.deduplication.DeduplicationParam;
import top.lunarye.handler.deduplication.service.AbstractDeduplicationService;
import top.lunarye.support.utils.RedisUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 简单去重
 * 用于固定间隔时间内相同内容去重
 * @author lunar
 * @since 2022-07-20
 */
@Service("SimpleLimitService")
public class SimpleLimitService extends AbstractLimitService {

    private static final String LIMIT_TAG = "SP_";

    @Resource
    private RedisUtils redisUtils;

    /**
     * 使用redis中的键过期机制完成去重键值的存储
     * @param service 去重器对象
     * @param taskInfo 任务信息
     * @param param 去重参数
     * @return 需要去重的用户列表
     */
    public Set<String> limitFilter(AbstractDeduplicationService service, TaskInfo taskInfo, DeduplicationParam param) {
        var filterReceiver = new HashSet<String>(taskInfo.getReceiver().size());
        // 获取redis中存储的去重记录
        var readyPutRedisReceiver = new HashMap<String, String>(taskInfo.getReceiver().size());
        // 得到进行去重服务时要用到的key值
        var keys = deduplicationAllKey(service, taskInfo).stream()
                .map(key -> LIMIT_TAG + key).toList();
        // 会依据放入查询的列表中的字符串进行查询，返回的结果；列表中没有查询到结果的会插入一个空值
        // 因为限定了接受人的信息，所以就将其一次性完全查询出来，方便后面进行处理
        var inRedisValue = redisUtils.mGet(keys);

        for (String receiver : taskInfo.getReceiver()) {
            // 依据去重规则构建去重的键值，去重的规则有两种：
            // 1. 依据发送的内容进行去重，其key: md5(templateId + receiver + content)
            // 2. 依据业务规则进行去重，其key ： receiver + templateId + sendChannel
            var key = LIMIT_TAG + deduplicationSingleKey(service, taskInfo, receiver);
            var value = inRedisValue.get(key);

            if (value != null && Integer.parseInt(value) >= param.getCountNum()) {
                filterReceiver.add(receiver);
            } else {
                readyPutRedisReceiver.put(receiver, key);
            }
        }

        // 不符合条件的用户：需要更新Redis(无记录添加，有记录则累加次数)
        // 就算有记录，但是还是需要在插入去重记录的时候更新时间
        putInRedis(readyPutRedisReceiver, inRedisValue, param.getDeduplicationTime());

        return filterReceiver;
    }

    /**
     * 存入redis 实现去重
     * @param readyPutRedisReceiver
     */
    private void putInRedis(Map<String, String> readyPutRedisReceiver,
                            Map<String, String> inRedisValue, Long deduplicationTime) {
        Map<String, String> keyValues = new HashMap<>(readyPutRedisReceiver.size());
        for (Map.Entry<String, String> entry : readyPutRedisReceiver.entrySet()) {
            String key = entry.getValue();
            if (inRedisValue.get(key) != null) {
                keyValues.put(key, String.valueOf(Integer.parseInt(inRedisValue.get(key)) + 1));
            } else {
                keyValues.put(key, String.valueOf(MessageConstant.TRUE));
            }
        }
        if (CollUtil.isNotEmpty(keyValues)) {
            redisUtils.pipelineSetEx(keyValues, deduplicationTime);
        }
    }
}
